package com.gfl.trivialrestclient.model;

import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;

import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.commons.lang.builder.ToStringBuilder;

@NamedQueries({ @NamedQuery(name = "User.findByUsername", query = "select u from User u where u.login = :username") })
@Entity
@Table(name = "USERS")
public class User implements Serializable {

	private static final long serialVersionUID = 1L;

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	@Column(name = "USER_ID", nullable = false)
	private Long id;

	@Column(name = "LOGIN", length = 50, nullable = false, unique = true)
	private String login;

	@Column(name = "PASSWORD", length = 50, nullable = false)
	private String password;

	@Column(name = "EMAIL", length = 200, nullable = false)
	private String email;

	// unidirectional relation with no removal cascade
	@ManyToMany(cascade = { CascadeType.PERSIST, CascadeType.MERGE,
			CascadeType.REFRESH }, fetch = FetchType.EAGER)
	@JoinTable(name = "USER_HAS_ROLES", joinColumns = { @JoinColumn(name = "USER_ID", referencedColumnName = "USER_ID") }, inverseJoinColumns = { @JoinColumn(name = "ROLE_ID", referencedColumnName = "ROLE_ID") })
	private Set<Role> roles = new HashSet<Role>();

	@Column(name = "ENABLED", nullable = false)
	private Boolean enabled;

	@Column(name = "LOCKED", nullable = false)
	private Boolean locked;

	protected User() {
	}

	public User(String login, String password, String email) {
		this(login, password, email, true, false);
	}

	public User(String login, String password, String email, Boolean enabled,
			Boolean locked) {
		this.login = login;
		this.password = password;
		this.email = email;
		this.enabled = enabled;
		this.locked = locked;
	}

	public Long getId() {
		return id;
	}

	public String getLogin() {
		return login;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	public String getEmail() {
		return email;
	}

	public Boolean getEnabled() {
		return enabled;
	}

	public Boolean getLocked() {
		return locked;
	}

	public Set<Role> getRoles() {
		return roles;
	}

	public void addRole(Role role) {
		if (!getRoles().contains(role)) {
			getRoles().add(role);
		}
	}

	public void removeRole(Role role) {
		if (getRoles().contains(role)) {
			getRoles().remove(role);
		}
	}

	/**
	 * Equals based on business key: login and email pair
	 */
	@Override
	public boolean equals(Object obj) {
		if (this == obj) {
			return true;
		}
		if (obj == null) {
			return false;
		}
		if (!(obj instanceof User)) {
			return false;
		}
		final User other = (User) obj;
		return new EqualsBuilder().append(getLogin(), other.getLogin())
				.append(getEmail(), other.getEmail()).isEquals();
	}

	@Override
	public int hashCode() {
		return new HashCodeBuilder().append(getLogin()).append(getEmail())
				.toHashCode();
	}

	@Override
	public String toString() {
		return ToStringBuilder.reflectionToString(this);
	}

}
